﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ERF {
    /// <summary>
    /// Represents a strongly typed collection, which buffers pending changes and applies them all at once.
    /// </summary>
    public class SynchronizedCollection<T> : IList<T> {

        /// <summary>
        /// Represents a pair of T and int, which describes on what index to add an object.
        /// </summary>
        private struct ObjectIndexPair {
            public T item;
            public int index;

            public ObjectIndexPair(T item, int index) {
                this.item = item;
                this.index = index;
            }

        }

        private List<ObjectIndexPair> pendingAdd;
        private List<T> pendingRemoval;
        private List<T> items;

        /// <summary>
        /// Creates a new instance SynchronizedCollection.
        /// </summary>
        public SynchronizedCollection() {
            this.pendingAdd = new List<ObjectIndexPair>();
            this.pendingRemoval = new List<T>();
            this.items = new List<T>();
        }

        #region IList<T> Members

        /// <summary>
        /// Returns the index in the collection of the given object. Returns -1 if it does not exist in the collection.
        /// </summary>
        /// <param name="item">The GameObject to search for.</param>
        /// <returns></returns>
        public int IndexOf(T item) {
            return this.items.IndexOf(item);
        }

        /// <summary>
        /// Inserts the given object on the given index in the collection.
        /// </summary>
        /// <param name="index">The index in the collection where the GameObject should be added.</param>
        /// <param name="item">The GameObject to add.</param>
        public void Insert(int index, T item) {
            this.pendingAdd.Add(new ObjectIndexPair(item, index));
        }

        /// <summary>
        /// Removes a object from the given index in the collection.
        /// </summary>
        /// <param name="index">The index of the GameObject to remove.</param>
        public void RemoveAt(int index) {
            this.pendingRemoval.Add(this.items[index]);
        }

        public T this[int index] {
            get {
                return this.items[index];
            }
            set {
                this.items[index] = value;
            }
        }

        #endregion

        #region ICollection<T> Members

        /// <summary>
        /// Adds an object to the list.
        /// </summary>
        /// <param name="item">The GameObject to add.</param>
        public void Add(T item) {
            this.pendingAdd.Add(new ObjectIndexPair(item, this.items.Count + this.pendingAdd.Count));
        }

        /// <summary>
        /// Clears all the obects from the collection.
        /// </summary>
        public void Clear() {
            List<int> l;
            this.pendingRemoval.AddRange(this);
        }

        /// <summary>
        /// Determines whether the given object is contained within this collection.
        /// </summary>
        /// <param name="item">The GameObject to locate.</param>
        /// <returns></returns>
        public bool Contains(T item) {
            return this.items.Contains(item);
        }

        /// <summary>
        /// Copies the entire collection to a one-dimensional array.
        /// </summary>
        /// <param name="array">The destination array.</param>
        /// <param name="arrayIndex">The index at which copying begins.</param>
        public void CopyTo(T[] array, int arrayIndex) {
            this.items.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Gets the number of objects in this collection.
        /// </summary>
        public int Count {
            get { return this.items.Count; }
        }

        /// <summary>
        /// Gets a value denoting if the collection is readonly.
        /// </summary>
        public bool IsReadOnly {
            get { return false; }
        }
        
        /// <summary>
        /// Removes the specified object from the collection.
        /// </summary>
        /// <param name="item">The GameObject to remove.</param>
        /// <returns>A boolean value denoting if the removal was a success.</returns>
        public bool Remove(T item) {
            if (this.items.Contains(item) &&
                !this.pendingRemoval.Contains(item))
                return false;
            else {
                this.pendingRemoval.Add(item);
                return true;
            }
            
        }

        #endregion

        #region IEnumerable<T> Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>An enumerator for the collection.</returns>
        public IEnumerator<T> GetEnumerator() {
            return this.items.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>An enumerator for the collection.</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return this.items.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Applies all the pending changes to the collection.
        /// </summary>
        public void ApplyChanges() {
            for (int i = 0; i < this.pendingAdd.Count; i++) {
                this.items.Insert(this.pendingAdd[i].index, this.pendingAdd[i].item);
            }
            this.pendingAdd.Clear();

            for (int i = 0; i < this.pendingRemoval.Count; i++) {
                this.items.Remove(this.pendingRemoval[i]);
            }

            this.pendingRemoval.Clear();
        }
    }
}
